module SuperMDA

push!(LOAD_PATH, joinpath(dirname(PROGRAM_FILE), "..", "models"))
push!(LOAD_PATH, joinpath(dirname(PROGRAM_FILE), "..", "models", "Parser"))

using Dates
using Random
using TOML

# basic
using XvgParser
using Vina
using Utils
using TopolParser
using XpmParser
using PdbParser
using GroParser
using IndexParser
using Tpr

include("../controllers/0/MDController.jl")
include("../controllers/1/TprController.jl")
include("../controllers/2/TopController.jl")
include("../controllers/3/XvgController.jl")
include("../controllers/4/XpmController.jl")
include("../controllers/5/PdbController.jl")
include("../controllers/6/GroController.jl")
include("../controllers/100/1_vina/VinaController.jl")


supported_filetype = ["tpr", "itp", "xvg", "txt", "xpm", "pdb", "gro", "ndx"]
wives = ["ZhangYang", "ZhangYang", "ZhangYang", "ZhangYang", "WangBingBing", "WangBingBing", "ZhuangXiaoYing", "BianJianXiaoHua"]

function julia_main()::Cint
    welcome()
    settings_path = ""
    if "settings.ini" in readdir(pwd())
        settings_path = joinpath(pwd(), "settings.ini")
        print("Found settings.ini in the current path. ")
    else
        if "SuperMDAPath" in keys(ENV) && "settings.ini" in readdir(ENV["SuperMDAPath"])
            settings_path = joinpath(ENV["SuperMDAPath"], "settings.ini")
            print("Found settings.ini in \$SuperMDAPath. ")
        else
            print("settings.ini not found either here or in environment virable \$SuperMDAPath. ")
        end
    end
    settings = Dict()
    if !isempty(settings_path)
        settings = TOML.parsefile(settings_path)
        println("Using $(settings["environments"]["nkernels"]) kernels.")
    else
        println("Using 1 kernel.")
    end
    is_reload = false
    while true
        xmp_file = "$(rand(wives)).$(rand(supported_filetype))"
        input_hint(xmp_file)
        infilename = initialize(settings, is_reload)
        if length(infilename) != 0
            if !test_infile_validity(infilename)
                return 0
            end
            infile = load_input_file(infilename, settings)
            if !isempty(settings_path)
                change_last_file(settings_path, infilename)
            end
            # 主循环
            is_reload = main_func_loop(infile, infilename, settings)
        else
            is_reload = main_func_loop()
        end
    end
    return 0 # if things finished successfully
end


function welcome()
    println(
        h("SuperMDA -- Supernova's Molecular Dynamics Analyzer\n", fc="c"),
        "Version v0.6.3, release date: 2022-Jul-12\n",
        "Developer: Jiaxing Zhang (@Supernova, $(u("https://www.zhihu.com/people/zhang-jia-xing-42-34")))\n",
        "( Current date: ", Date(Dates.now()), " ", Time(Dates.now()), " )\n",
        h("\nHint: ", fc="c") * "You can add the SuperMDA/bin folder to the environment virable PATH\n",
        "and execute " * h("`SuperMDA $(rand(wives)).$(rand(supported_filetype))`", fc="r") * " in the working directory directly.\n"
    )
end

function input_hint(xmp_file)
    println(
        h("Input file path, ", fc="b"), "e.g., E:/Data/$xmp_file\n",
        "(Supported: $(join(supported_filetype, " ")),\n",
        "where the txt file was the docking config file generated by AutoDockTools)\n",
        "To reload the file last time used, simply input the letter \"o\".\n",
        "Input such as ?$xmp_file can open $xmp_file in the same directory as the file last time used."
    )
end


function initialize(settings, is_reload=false)::String
    if is_reload || length(ARGS) == 0
        # 手动输入
        input = get_input("")
    else
        # 命令行传入
        input = ARGS[1]
    end
    if startswith(input, "?")
        if isempty(settings)
            error_hint("Cannot find settings.ini, so last file unknown.\n")
            exit(1)
        end
        input = joinpath(dirname(settings["others"]["last_opened"]), input[2:end])
    end
    infilename = ""
    if !isempty(input)
        if isfile(input)
            infilename = abspath(replace(input, "\\" => "/"))
        elseif input == "o"
            # 加载上次的路径
            infilename = settings["others"]["last_opened"]
        else
            error_hint("Error: Input is not a valid file: $input")
        end
    end
    return infilename
end


function test_infile_validity(infilename)
    if !(split(infilename, ".")[end] in supported_filetype)
        error_hint("SuperMDA cannot handle this file type currently.\n")
        return false
    end
    return true
end


function load_input_file(infilename, settings)
    # 读取文件
    if endswith(infilename, ".xvg")
        return XvgParser.read_xvg(infilename)
    elseif endswith(infilename, ".tpr")
        return Tpr.load_tpr(infilename, settings)
    elseif endswith(infilename, ".itp")
        return TopolParser.gen_topol_from_file(infilename)
    elseif endswith(infilename, ".txt")
        # TODO: config.txt合理性测试
        return Vina.read_config(infilename)
    elseif endswith(infilename, ".xpm")
        return XpmParser.read_xpm(infilename)
    elseif endswith(infilename, ".pdb")
        return PdbParser.gen_pdb_from_file(infilename)
    elseif endswith(infilename, ".gro")
        return GroParser.gen_gro_from_file(infilename)
    elseif endswith(infilename, ".ndx")
        return IndexParser.gen_ndx_from_file(infilename)
    end
end

function change_last_file(settings_path, infilename)
    # 修改配置
    if !isempty(settings_path)
        lines = readlines(settings_path)
        lines[findfirst(startswith("last_opened"), lines)] = "last_opened=\"$(replace(abspath(infilename), "\\" => "/"))\""
        write(settings_path, join(lines, "\n"))
    end
end


function main_func_loop(infile="", infilename::String="", settings=Dict())
    while true
        main_func = strip(list_main_funcs())
        if main_func == "0"
            main_func_0()
        elseif main_func == "1"
            main_func_1(infile, infilename, settings)
        elseif main_func == "2"
            main_func_2(infile, infilename)
        elseif main_func == "3"
            main_func_3(infile, infilename)
        elseif main_func == "4"
            main_func_4(infile, infilename)
        elseif main_func == "5"
            main_func_5(infile, infilename)
        elseif main_func == "6"
            main_func_6(infile, infilename)
        elseif main_func == "100"
            main_func_100(infile, infilename)
        elseif main_func == "r"
            if Sys.iswindows()
                run(`cmd /c cls`)
            elseif Sys.islinux()
                run(`clear`)
            end
            return true
        elseif main_func == "mi"
            MDRoutine.generate_mdp()
        elseif main_func == "ti"
            MDRoutine.generate_top()
        elseif main_func == "q"
            exit(0)
        else
            error_hint("Error input: $main_func, select again.")
        end
    end
end


function list_main_funcs()
    println("\n\"r\" -- reload another file.
\"mi\" -- generate mdp file templates.
\"ti\" -- generate top file templates.
\"q\" -- exit program elegantly.
")
    println()
    println(h("                    ************ Main function menu ************", fc="c"))
    println(" 0 Working with Molecular Dynamics protocol")
    println(" 1 Molecular Mechanics-Poisson Boltzmann Surface Area (MM-PBSA) analysis")
    println(" 2 Working with topology file (itp)")
    println(" 3 Working with XVG data file")
    println(" 4 Working with XPM data file")
    println(" 5 Working with PDB file")
    println(" 6 Working with GRO file")
    println(" 100 Other functions")
    return sel_fun()  
end


# Main function 0: Basic analysis
function main_func_0()
    MDRoutine.md_functions()
end


# Main function 1: MMPBSA working
function main_func_1(mdp, tprfilename, settings)
    TprRoutine.tpr_functions(mdp, tprfilename, settings)
end


# Main function 2: topology reader
function main_func_2(topfile, topfilename)
    TopRoutine.top_functions(topfile, topfilename)
end


# Main function 3: XVG working
function main_func_3(xvgfile::XVG, xvgfilename::String)
    XvgRoutine.xvg_functions(xvgfile, xvgfilename)
end


# Main function 4: XPM working
function main_func_4(xpmfile, xpmfilename)
    XpmRoutine.xpm_functions(xpmfile::XPM, xpmfilename::String)
end


# Main function 5: PDB working
function main_func_5(pdbfile, pdbfilename)
    PdbRoutine.pdb_functions(pdbfile, pdbfilename)
end


# Main function 6: GRO working
function main_func_6(grofile, grofilename)
    GroRoutine.gro_functions(grofile, grofilename)
end


# Main function 100: Other functions
function main_func_100(infile, infilename::String)
    println(h("\n                    ************ Other functions ************", fc="c"))
    println("0 Return to main menu")
    println("1 Working with audotock vina")
    input = parse(Int32, readline())
    if input == 0
        return
    elseif input == 1
        VinaRoutine.vina_functions(infile, infilename)
    end
end

if abspath(PROGRAM_FILE) == @__FILE__
    julia_main()
end

end
